package org.ecpkn.PAGridSim.core.gridsimEntity.policy.paPolicy.concretePolicy.RMPartitionPAPolicy;

import gridsim.GridSim;
import gridsim.Gridlet;
import gridsim.PE;
import gridsim.PEList;
import gridsim.ResGridlet;

import java.util.Comparator;

import org.ecpkn.PAGridSim.core.gridsimEntity.paMachine.PAMachine;
import org.ecpkn.PAGridSim.core.gridsimEntity.policy.paPolicy.PartitionNonPreemptablePAPolicy;
import org.ecpkn.PAGridSim.core.gridsimEntity.policy.paPolicy.priority.RM.RM;
import org.ecpkn.PAGridSim.core.tasksCreation.Job;
import org.ecpkn.PAGridSim.core.tasksCreation.Task;

/**
 * @author Lin Zhou
 * 
 *         This class extends NonPreemptablePartitionPAPolicy and the schedule
 *         policy is based on RM
 * 
 */
public class RMPartitionNonPreemptablePAPolicy extends
		PartitionNonPreemptablePAPolicy implements RM{

	public RMPartitionNonPreemptablePAPolicy(String resourceName,
			String policyName) throws Exception {
		super(resourceName, policyName);
	}

	/**
	 * Allocates a Gridlet into a free PE and sets the Gridlet status into
	 * INEXEC and PE status into busy afterwards
	 * 
	 * @param rgl
	 *            a ResGridlet object
	 * @param freeMachineID
	 * @return <tt>true</tt> if there is an empty PE to process this Gridlet,
	 *         <tt>false</tt> otherwise
	 * @pre rgl != null
	 */
	protected boolean allocatePEtoGridlet(ResGridlet rgl, int taskMachineID,
			int freeMachineID) {
		// IDENTIFY MACHINE which has a free PE and add this Gridlet to it.
		Job job = (Job) rgl.getGridlet();
		PAMachine myMachine = getMachineForGridlet(taskMachineID,
				freeMachineID, job);

		// If no Machine is empty then ignore the rest
		if (myMachine == null) {
			return false;
		}
		// gets the list of PEs and find one empty PE
		PEList MyPEList = myMachine.getPEList();
		int freePE = MyPEList.getFreePEID();
		// Identify Completion Time and Set Failed if necessary
		int rating = myMachine.getMIPSRating();
		double execuTime = forecastFinishTime(rating,
				rgl.getRemainingGridletLength());
		double roundUpTime = execuTime + GridSim.clock() + 0.001;
		// we need a rounding up here, because if we send the finish signal too
		// early, and actually the job has not yet been finished,it will lost
		// the first chance of finish check and it has to wait for the next one
		// to give a signal.In some cases,the next one come too late,which can
		// cause it to lose its deadline
		if (taskMachineID == Const.TASK_NOTYET_ALLOCATED_TO_MACHINE) {
			// new task to a machine,we should update the machine utilization
			myMachine.changeSingleTaskMachineUtilization(execuTime
					/ job.getPeriode_());
		}
		// should we allow a job to continue when we are sure that it will fail
		// if (job.getDeadline_() < roundUpTime) {
		// rgl.setGridletStatus(Gridlet.FAILED);
		// return false;
		// }
		// ALLOCATE IMMEDIATELY
		rgl.setGridletStatus(Gridlet.INEXEC); // change Gridlet status
		rgl.setMachineAndPEID(myMachine.getMachineID(), freePE);
		// add this Gridlet into execution list
		gridletInExecList_.add(rgl);
		// Set allocated PE to BUSY status
		super.resource_.setStatusPE(PE.BUSY, rgl.getMachineID(), freePE);
		rgl.setFinishTime(roundUpTime);
		// then send this into itself
		super.sendInternalEvent(roundUpTime - GridSim.clock());
		return true;
	}

	/**
	 * 
	 * @param taskMachineID
	 * @param freeMachineID
	 * @param job
	 * @return
	 */
	private PAMachine getMachineForGridlet(int taskMachineID,
			int freeMachineID, Job job) {
		// TODO:add partition global
		Task task = job.getTask_();
		PAMachine m = (PAMachine) resource_.getMachineList().getMachine(
				freeMachineID);

		// just want a machine,no limitation of ID
		if (taskMachineID == Const.TASK_NOTYET_ALLOCATED_TO_MACHINE) {

			boolean allow = loadBalanceAllowed(freeMachineID, job);
			if (allow == true) {
				m = this.getAwakePAMachineList().getMachine(freeMachineID);
				m.addTaskToMachine(task);
				return m;
			}
		} else {
			m = (PAMachine) resource_.getMachineList()
					.getMachine(taskMachineID);
			if (m.getNumFreePE() != 0) {
				return m;
			}
		}
		return null;
	}

	@Override
	protected Comparator<ResGridlet> getPriorityComparator() {
		return RM.comparator;
	}

}
